#pragma once

#include "sfc/core.h"

namespace sfc::math {

static constexpr f64 PI = 3.14159265358979323846;

using num::ceil;
using num::fabs;
using num::floor;
using num::fmax;
using num::fmin;
using num::fmod;
using num::round;

[[sfc_inline]] inline auto exp(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_expf(x);
  } else {
    return __builtin_exp(x);
  }
}

[[sfc_inline]] inline auto log(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_logf(x);
  } else {
    return __builtin_log(x);
  }
}

[[sfc_inline]] inline auto sqrt(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_sqrtf(x);
  } else {
    return __builtin_sqrt(x);
  }
}

[[sfc_inline]] inline auto pow(trait::Float auto x, trait::Float auto y) {
  if constexpr (__is_same(decltype(x + y), f32)) {
    return __builtin_powf(x, y);
  } else {
    return __builtin_pow(x, y);
  }
}

[[sfc_inline]] inline auto hypot(trait::Float auto x, trait::Float auto y) {
  if constexpr (__is_same(decltype(x + y), f32)) {
    return __builtin_hypotf(x, y);
  } else {
    return __builtin_hypot(x, y);
  }
}

[[sfc_inline]] inline auto cos(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_cosf(x);
  } else {
    return __builtin_cos(x);
  }
}

[[sfc_inline]] inline auto sin(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_sinf(x);
  } else {
    return __builtin_sin(x);
  }
}

[[sfc_inline]] inline auto tan(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_tanf(x);
  } else {
    return __builtin_tan(x);
  }
}

[[sfc_inline]] inline auto acos(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_acosf(x);
  } else {
    return __builtin_acos(x);
  }
}

[[sfc_inline]] inline auto asin(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_asinf(x);
  } else {
    return __builtin_asin(x);
  }
}

[[sfc_inline]] inline auto atan(trait::Float auto x) {
  if constexpr (__is_same(decltype(x), f32)) {
    return __builtin_atanf(x);
  } else {
    return __builtin_atan(x);
  }
}

[[sfc_inline]] inline auto atan2(trait::Float auto x, trait::Float auto y) {
  if constexpr (__is_same(decltype(x + y), f32)) {
    return __builtin_atan2f(x, y);
  } else {
    return __builtin_atan2(x, y);
  }
}

[[sfc_inline]] inline auto deg2rad(trait::Float auto x) {
  static constexpr auto K = static_cast<decltype(x)>(PI / 180.0);
  return K * x;
}

[[sfc_inline]] inline auto rad2deg(trait::Float auto x) {
  static constexpr auto K = static_cast<decltype(x)>(180.0 / PI);
  return K * x;
}

}  // namespace sfc::math
